useLayoutEffect is a React Hook that fires synchronously after all DOM mutations but before the browser paints the screen, while useEffect fires asynchronously after the paint. This difference makes useLayoutEffect crucial for DOM measurements and preventing visual flicker, but useEffect is the default for most side effects due to better performance.
The core difference between useLayoutEffect and useEffect is the timing of their execution. useEffect runs asynchronously after the browser has finished painting the screen, meaning the user might see a brief flash of the initial state before the effect applies changes. useLayoutEffect, however, runs synchronously immediately after React has calculated all DOM mutations but before the browser paints, allowing you to make changes that are visible in the very first frame.
To visualize the order: when a component renders, React first updates the DOM, then useLayoutEffect fires, then the browser paints, and finally useEffect fires. This means useLayoutEffect blocks the browser's paint until it completes, which is why heavy computations in it can hurt performance, while useEffect lets the browser render immediately and does its work afterward.
Measuring DOM elements: When you need to get an element's dimensions (getBoundingClientRect(), offsetWidth) for calculations that affect the initial layout.
Preventing visual flicker: When making DOM changes that would be visible if they occurred after paint (e.g., adjusting scroll position, setting focus).
Synchronizing animations: When you need to read or write layout properties in the same frame to prevent layout thrashing.
Auto-resizing textareas: Calculating and applying new heights based on content before the user sees any height jump.
Data fetching: API calls, loading data from servers
Setting up subscriptions: Event listeners, WebSocket connections
Updating non-visual state: Document title, localStorage, analytics
Logging and analytics: Tracking page views or user interactions
Any side effect that doesn't need to block the user's view
Because useLayoutEffect is synchronous and blocks the browser's paint, it can degrade performance if used unnecessarily or if it contains expensive computations. The rule of thumb is: default to useEffect and only use useLayoutEffect when you need to read or write DOM layout before the user sees the screen. Overusing useLayoutEffect can lead to layout thrashing, where the browser is forced to repeatedly recalculate styles and layout, causing jank and poor frame rates.
useLayoutEffect does not run on the server during SSR because it requires the DOM to be present. If you're using Next.js or any SSR setup, React will log a warning when useLayoutEffect is used on the server. For code that runs in both environments, you can either use useEffect (which is safe for SSR) or conditionally import useLayoutEffect only on the client side.
Execution Timing: useEffect runs after paint (async); useLayoutEffect runs before paint (sync)
Blocking Nature: useEffect is non-blocking; useLayoutEffect blocks the browser from painting until it completes
Use Cases: useEffect for most side effects (data fetching, subscriptions); useLayoutEffect for DOM measurements and preventing flicker
Performance Impact: useEffect is optimized for performance; useLayoutEffect can cause jank if overused
SSR Compatibility: useEffect works on server; useLayoutEffect does not
Equivalent Lifecycles: useLayoutEffect timing matches componentDidMount/componentDidUpdate; useEffect runs after paint